#include "common.h"

#define MAX_LINE 4096

char rot13_char(char c)
{
    if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
        return c + 13;
    else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
        return c - 13;
    else
        return c;
}

void child_run(int fd)
{
    char outbuf[MAX_LINE + 1];
    size_t outbuf_used = 0;
    ssize_t result;
    while (1)
    {
        char ch;
        result = recv(fd, &ch, 1, 0); // 每次接受一个字符
        if (result == 0)
        {
            break; // 没有数据了，直接 break
        }
        else if (result == -1)
        {
            perror("read");
            break; // 读取失败，直接 break
        }
        if (outbuf_used < sizeof(outbuf))
        {
            outbuf[outbuf_used++] = rot13_char(ch);
        }
        // 遇到换行
        // 读取客户端发送的数据，待读完一行后，再向客户端发送
        if (ch == '\n')
        {
            send(fd, outbuf, outbuf_used, 0); // 发送数据
            outbuf_used = 0;
            continue;
        }
    }
}

void sigchld_handler(int sig)
{
    // -1     meaning wait for any child process.
    // WNOHANG     return immediately if no child has exited.
    while (waitpid(-1, 0, WNOHANG) > 0)
        ;
    return;
}

int main(int argc, char const *argv[])
{
    int port = 3000;
    int listen_fd = tcp_server_listen(port);
    signal(SIGCHLD, sigchld_handler); // 注册信号处理函数
    printf("listening at localhost:%d\n", port);
    // 循环处理请求
    for (;;)
    {
        struct sockaddr_in client;
        socklen_t clilen;
        clilen = sizeof(client);
        int connfd = accept(listen_fd, (struct sockaddr *)&client, &clilen);
        if (connfd < 0)
        {
            error(1, errno, "accept failed");
            exit(EXIT_FAILURE);
        }
        // fork 调用一次，却返回两次
        if (fork() == 0)
        {
            // 在子进程中
            close(listen_fd);   // 由于，子进程复制了父进程的fd，所以额外关闭 listen
            child_run(connfd);  // 处理客户端连接
            exit(EXIT_SUCCESS); // 在子进程中退出
        }
        else // 在父进程中退出，直接 close 客户端连接
        {
            close(connfd);
        }
    }
    return 0;
}
